package com.momo.hystrix.service;

import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
import com.netflix.hystrix.contrib.javanica.cache.annotation.CacheKey;
import com.netflix.hystrix.contrib.javanica.cache.annotation.CacheRemove;
import com.netflix.hystrix.contrib.javanica.cache.annotation.CacheResult;
import com.netflix.hystrix.contrib.javanica.command.AsyncResult;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;

import java.util.concurrent.Future;

@Service
public class HelloService {

    @Autowired
    RestTemplate restTemplate;

    /**
     * 在这个方法中我们回发起一个远程调用，去掉硬的provider中提供的/hello 远程接口
     * 但是这个调用可能会失败，（比如说网络原因。。等等）所以我们要使用Hystrix
     * @HystrixCommand 是一种服务降级（熔断） 会调用fallbackMethod 指定的方法
     * @ignoreExceptions 表示忽略一种一场，如果这个方法抛出这种异常 ，不要进行服务降级，直接抛出异常
     * @return
     */
    @HystrixCommand(fallbackMethod = "error",ignoreExceptions = ArithmeticException.class)
    public String hello(){
        int  i = 1 /0 ;//这里就是忽略了数学异常 除数能为0
        return restTemplate.getForObject("http://my-provider/hello",String.class);

    }


    /**
     * 使用注解的方式继续熔断方法的异步调用
     * 1、使用java中的Future异步人物来实现
     * 2、重写AsyncResult 中的invoke 方法
     * 3、而这个invoke中的方法就是我们要执行的方法
     * @return
     */
    @HystrixCommand()
    public Future<String> hello2(){
        return new AsyncResult<String>(){
            @Override
            public String invoke() {
                return restTemplate.getForObject("http://my-provider/hello",String.class);
            }
        };
    }


    /**
     *
     * @param name 带有缓存的请求
     * @return
     */
    @HystrixCommand(fallbackMethod = "errorWithCacheRemove")
    @CacheResult//表示这个注解该方法的缓存结果会被缓存起来，默认情况下缓存的Key是方法的参数，缓存的value 是方法的返回值
    public String hello3WithRequestCache(String name){
        String forObject = restTemplate.getForObject("http://my-provider/hello2?name={1}", String.class, name);
        return forObject;
    }


    /**
     *
     * @param name 带有缓存的请求
     * @return forObject 返回指定的key
     * @CacheKey 指定缓存的Key
     */
    @HystrixCommand(fallbackMethod = "errorOfHello3")
    @CacheResult//表示这个注解该方法的缓存结果会被缓存起来，默认情况下缓存的Key是方法的参数，缓存的value 是方法的返回值
    public String hello3WithRequestCacheByCacheKey(@CacheKey String name,Integer age){
        String forObject = restTemplate.getForObject("http://my-provider/hello2?name={1}", String.class, name);
        return forObject;
    }

    /**
     *
     * @param name 带有缓存的请求
     * @CacheKey 指定缓存的Key
     * @CacheRemove 在使用CacheRemove的时候，我们必须指定commandKey 属性，因为commandKey 就是缓存的方法,通过这个key来删除对应缓存
     */
    @HystrixCommand
    @CacheRemove(commandKey = "hello3WithRequestCache")//如果是一个删除的操作，我们不但要删除数据库的同时也要删除缓存的数据（做一个缓存和数据的操作）
    public String deleteUserWithRequestCacheByCacheRemove(String name){
        /*
         * 这里可以写一些常用的数据删除的操作可以放到这里来写
         */
        System.out.println(name);
        return null;
    }


    /**
     * 注意这里error方法 必须要和原调用方法的返回值一致[这个方法对应hello]
     * @param throwable  是为了区分到底是consumer代码出错
     * 还是调用provider出错的。（打印出具体的错误信息）
     */
    public String errorHello1(Throwable throwable){
        return "错误降级的方法" + throwable.getMessage();
    }

    public String errorWithCacheRemove(String name,Throwable throwable){
        return "使用了请求缓存的错误降级的方法" + name + throwable.getMessage();
    }

    public String errorOfHello3(String name,Integer age,Throwable throwable){
        return "使用了请求缓存的错误降级的方法" + name + age+ throwable.getMessage();
    }
}
